package references

import (
	"jvm/instructions/base"
	"jvm/rtda"
	"jvm/rtda/heap"
)

// **************************************************
// 指令码后两字节为运行时常量池索引,根据索引取出类符号引用。
// 解析类符号引用得到类数据，根据类数据创建类对象，将对象压入栈顶
type NEW struct {
	base.U2IndexInst
}

func (self *NEW) Execute(frame *rtda.Frame) {
	cp := frame.Method().Class().ConstantPool()
	classRef := cp.GetConstant(self.Index).(*heap.ClassRef)
	newClass := classRef.ResolvedClass()
	if !newClass.IsInitialization() {
		frame.RevertNextPC()
		base.InitClass(frame.Thread(), newClass)
		return
	}
	if newClass.IsAbstract() || newClass.IsInterface() {
		panic("java.lang.InstantiationError")
	}
	obj := newClass.NewObject()
	frame.OPStack().PushRef(obj)
}

// **************************************************
// 指令用于获得某个类的静态变量值
// 操作数为U2的运行时常量池索引
// 根据索引获取字段引用，解析字段引用获取字段信息，判断该字段是否能被
// 合法访问，不能则报错。根据该字段的descriptor判断字段类型，
// 从字段所在类的staticVars中获得索引为Field.SlotId的值，将值压栈
type GET_STATIC struct {
	base.U2IndexInst
}

func (self *GET_STATIC) Execute(frame *rtda.Frame) {
	fieldRef := frame.Method().Class().ConstantPool().GetConstant(self.Index).(*heap.FieldRef)
	field := fieldRef.ResolvedField()
	newClass := field.Class()
	if !newClass.IsInitialization() {
		frame.RevertNextPC()
		base.InitClass(frame.Thread(), newClass)
		return
	}
	if !field.IsStatic() {
		panic("java.lang.IncompatibleClassChangeError")
	}
	staticVars := field.Class().StaticVars()
	stack := frame.OPStack()
	slotId := field.SlotId()
	switch field.Descriptor() {
	case "Z", "B", "S", "C", "I":
		stack.PushInt(staticVars.GetInt(slotId))
	case "J":
		stack.PushLong(staticVars.GetLong(slotId))
	case "F":
		stack.PushFloat(staticVars.GetFloat(slotId))
	case "D":
		stack.PushDouble(staticVars.GetDouble(slotId))
	default:
		stack.PushRef(staticVars.GetRef(slotId))
	}
}

// **************************************************
//指令用于修改某个类的静态变量值
//操作数为U2类型的运行时常量池索引(字节码中),要修改的值(操作数栈顶)
//根据索引找到字段引用，解析字段引用获取字段信息，判断该字段是否能被
//合法访问，不能则报错。根据该字段的descriptor判断字段类型，从操作数
//栈将该类型变量弹出，放入以字段的SlotId为索引的字段所在类的staticVars中

type PUT_STATIC struct {
	base.U2IndexInst
}

func (self *PUT_STATIC) Execute(frame *rtda.Frame) {
	cp := frame.Method().Class().ConstantPool()
	fieldRef := cp.GetConstant(self.Index).(*heap.FieldRef)
	field := fieldRef.ResolvedField()
	newClass := field.Class()
	if !newClass.IsInitialization() {
		frame.RevertNextPC()
		base.InitClass(frame.Thread(), newClass)
		return
	}
	//操作非static变量会报错
	if !field.IsStatic() {
		panic("java.lang.IncompatibleClassChangeError")
	}
	class := field.Class()
	//在非<clinit>函数中操作static final变量会报错
	if field.IsFinal() {
		if frame.Method().Class() != class || frame.Method().Name() != "<clinit>" {
			panic("java.lang.IllegalAccessError")
		}
	}

	staticVars := class.StaticVars()
	slotId := field.SlotId()
	stack := frame.OPStack()
	switch field.Descriptor() {
	case "Z", "B", "S", "C", "I":
		staticVars.SetInt(slotId, stack.PopInt())
	case "J":
		staticVars.SetLong(slotId, stack.PopLong())
	case "F":
		staticVars.SetFloat(slotId, stack.PopFloat())
	case "D":
		staticVars.SetDouble(slotId, stack.PopDouble())
	default:
		staticVars.SetRef(slotId, stack.PopRef())
	}

}

// **************************************************
// 获取对象字段的值，压入栈顶。
// 需要两个操作数。
// 1.U2的运行时常量池索引(字节码)。
// 2.对象引用(操作数栈顶)。
// 根据索引获取字段符号引用，解析得到字段，判断字段是否合法。
// 不合法报错。弹出对象引用，根据字段类型和SlotId从对象字段中
// 拿出值压入栈中。
type GET_FIELD struct {
	base.U2IndexInst
}

func (self *GET_FIELD) Execute(frame *rtda.Frame) {
	fieldRef := frame.Method().Class().ConstantPool().GetConstant(self.Index).(*heap.FieldRef)
	field := fieldRef.ResolvedField()
	if field.IsStatic() {
		panic("java.lang.IncompatibleClassChangeError")
	}
	stack := frame.OPStack()
	ref := stack.PopRef()
	if ref == nil {
		panic("java.lang.NullPointerException")
	}
	instanceSlots := ref.Fields()
	slotId := field.SlotId()
	switch field.Descriptor() {
	case "Z", "B", "S", "C", "I":
		stack.PushInt(instanceSlots.GetInt(slotId))
	case "J":
		stack.PushLong(instanceSlots.GetLong(slotId))
	case "F":
		stack.PushFloat(instanceSlots.GetFloat(slotId))
	case "D":
		stack.PushDouble(instanceSlots.GetDouble(slotId))
	default:
		stack.PushRef(instanceSlots.GetRef(slotId))
	}

}

// **************************************************
// 用于给对象的实例字段赋值
// 有三个操作数
// 1.U2的常量池索引(来自字节码)
// 2.要赋的值(操作数栈顶)
// 3.对象引用(操作数次栈顶)
// 根据常量池索引拿到字段引用并解析，判断解析出的字段是否合法
// 不合法则报错。合法则根据字段类型弹出要赋的值，再弹出对象引用.
// 若对象引用为空，报错。否则给其相应字段赋值。
type PUT_FIELD struct {
	base.U2IndexInst
}

func (self *PUT_FIELD) Execute(frame *rtda.Frame) {
	fieldRef := frame.Method().Class().ConstantPool().GetConstant(self.Index).(*heap.FieldRef)
	field := fieldRef.ResolvedField()
	if field.IsStatic() {
		panic("java.lang.IncompatibleClassChangeError")
	}
	if field.IsFinal() {
		currentClass := frame.Method().Class()
		if currentClass != field.Class() || frame.Method().Name() != "<init>" {
			panic("java.lang.IllegalAccessError")
		}
	}
	stack := frame.OPStack()
	slotId := field.SlotId()
	switch field.Descriptor() {
	case "Z", "B", "S", "C", "I":
		val := stack.PopInt()
		ref := stack.PopRef()
		if ref == nil {
			panic("java.lang.NullPointerException")
		}
		instanceSlots := ref.Fields()
		instanceSlots.SetInt(slotId, val)
	case "J":
		val := stack.PopLong()
		instanceSlots := stack.PopRef().Fields()
		instanceSlots.SetLong(slotId, val)
	case "F":
		val := stack.PopFloat()
		ref := stack.PopRef()
		if ref == nil {
			panic("java.lang.NullPointerException")
		}
		instanceSlots := ref.Fields()
		instanceSlots.SetFloat(slotId, val)
	case "D":
		val := stack.PopDouble()
		ref := stack.PopRef()
		if ref == nil {
			panic("java.lang.NullPointerException")
		}
		instanceSlots := ref.Fields()
		instanceSlots.SetDouble(slotId, val)
	default:
		val := stack.PopRef()
		ref := stack.PopRef()
		if ref == nil {
			panic("java.lang.NullPointerException")
		}
		instanceSlots := ref.Fields()
		instanceSlots.SetRef(slotId, val)
	}
}

// **************************************************
// 判断对象是否是类的实例
// 2个操作数 运行时常量池索引(字节码) 对象引用(操作数栈顶)
// 根据索引拿到类符号引用，解析后得到类数据。
// 判断对象是否是类的实例，将结果压栈。(0:不是 1:是)
type INSTANCE_OF struct {
	base.U2IndexInst
}

func (self *INSTANCE_OF) Execute(frame *rtda.Frame) {
	classRef := frame.Method().Class().ConstantPool().GetConstant(self.Index).(*heap.ClassRef)
	class := classRef.ResolvedClass()
	stack := frame.OPStack()
	obj := stack.PopRef()
	if obj == nil {
		stack.PushInt(0)
		return
	}
	if obj.IsInstanceOf(class) {
		stack.PushInt(1)
	} else {
		stack.PushInt(0)
	}
}

// **************************************************
// 检查对象是否可以转换成某一类型，不能则报错
// 有两个操作数 运行时常量池索引(字节码) 对象引用(栈顶)
// 用索引拿到类符号引用，解析，出栈拿到对象引用再入栈。
// 如果对象引用为空，可以转换。
// 如果对象是类的实例，可以转换
// 否则不能转换，报错
type CHECK_CAST struct {
	base.U2IndexInst
}

func (self *CHECK_CAST) Execute(frame *rtda.Frame) {
	classRef := frame.Method().Class().ConstantPool().GetConstant(self.Index).(*heap.ClassRef)
	class := classRef.ResolvedClass()
	stack := frame.OPStack()
	obj := stack.PopRef()
	stack.PushRef(obj)
	if obj == nil {
		return
	}
	if !obj.IsInstanceOf(class) {
		panic("java.lang.ClassCastException")
	}
}
